#include "memory.hpp"
#include "typedef.hpp"
#include "processor.hpp"
#include <fstream>

static const int next[4] = {3, 2, 0, 1};

MemControl membus;
extern Reg<uint64_t> sregs;
extern Reg<uint64_t> IMMs;
uint8_t *memory;
extern bool Error;

// Cache相关函数
// CacheBlock
bool CacheBlock::read(uint64_t tag, uint8_t offset)
{ // 读Cache
	for (int i = 0; i < 4; i++)
		if (mem[i].first == tag) // 行标记匹配
		{
			if (mem[i].second.MESImeta & I) // 行无效
				return false;
			memcpy(IMMs(MDR), &mem[i].second + offset, 8);
			return true;
		}
	return false;
}
bool CacheBlock::write(uint64_t tag, uint8_t offset, size_t size) // 写Cache
{
	for (int i = 0; i < 4; i++)
		if (mem[i].first == tag)
		{
			if (mem[i].second.MESImeta & I) // 行无效
				return false;
			mem[i].second.MESImeta = M; // 修改标记
			memcpy(&mem[i].second + offset, IMMs(MDR), size);
			return true;
		}
	return false;
}
void CacheBlock::load(uint64_t tag)
{
	if (mem[ptr].second.MESImeta & M) // 脏数据
		store(ptr);
	mem[ptr].first = tag;							   // 写入行标记
	memcpy(&mem[ptr].second, memory + (tag << 6), 64); // 复制数据
	mem[ptr].second.MESImeta = E;					   // 修改标记
	ptr = next[ptr];
}
void CacheBlock::store(uint64_t index)
{
	if (mem[index].second.MESImeta != M) // 无脏数据
		return;
	memcpy(memory + (mem[index].first << 6), &mem[index].second, 64);
	mem[index].second.MESImeta = I; // 修改标记
}
// CacheSet
bool CacheSet::read(uint64_t tag, uint8_t offset)
{
	uint64_t index = tag % count;
	return cache[index].read(tag, offset);
}
bool CacheSet::write(uint64_t tag, uint8_t offset, size_t size)
{
	uint64_t index = tag % count;
	return cache[index].write(tag, offset, size);
}
void CacheSet::load(uint64_t tag)
{
	uint64_t index = tag % count;
	cache[index].load(tag);
}
void CacheSet::store(uint64_t tag)
{
	uint64_t index = tag % count;
	cache[index].store(index);
}
// Cache
void Cache::read(bool isCommand)
{
	uint8_t offset = IMMs[MAR] & 0x3f;
	uint64_t tag = IMMs[MAR] >> 6;
	if (isCommand)
	{
		if (!L1I.read(tag, offset))
		{
			L1I.load(tag);
			L1I.read(tag, offset);
		}
	}
	else
	{
		if (!L1D.read(tag, offset))
		{
			L1D.load(tag);
			L1D.read(tag, offset);
		}
	}
}
void Cache::write(size_t size)
{
	uint8_t offset = IMMs[MAR] & 0x3f;
	uint64_t tag = IMMs[MAR] >> 6;
	if (!L1D.write(tag, offset, size))
	{
		L1D.load(tag);
		L1D.write(tag, offset, size);
	}
}
void Cache::load(bool isCommand)
{
	uint8_t offset = IMMs[MAR] & 0x3f;
	uint64_t tag = IMMs[MAR] >> 6;
	if (isCommand)
		L1I.load(tag);
	else
		L1D.load(tag);
}

// TLB相关函数
void TLB::addtrans(uint8_t access)
{
begin: // 查询流程
	uint64_t vpage = IMMs[MAR] >> 12;
	pair<bool, uint64_t> temp = (*this)[vpage];
	if (!temp.first) // 未命中
	{
	search:
		if (sregs[Cause] != 0) // 已经缺页
			return;
		update();
		goto begin;
	}
	else // 命中
	{
		if (!(getMeta(temp.second) & 0x02)) // 页表无效
			goto search;
		if ((access & getAccess(temp.second)) == access) // 权限校验通过
			IMMs[MAR] = (getPageID(temp.second) << 12) + IMMs[MAR] & 0xfff;
		else // 权限校验失败
		{
			Error = true;
			sregs[Cause] = 0xf101;
		}
	}
}
void TLB::update()
{
	uint64_t vpage = IMMs[MAR] >> 12;
	uint16_t vid[3] = { 0 };
	uint64_t pageItem;
	for (int i = 0; i<3; vpage >>= 9, i++)
		vid[i] = vpage & 0x1ff;	 // 虚拟页号拆分
	uint64_t addr = sregs[PDTR]; // 逐级地址寄存
	for (int i = 0; i < 3; i++)
	{
		addr += (uint64_t)(vid[i]) << 3;
		memcpy(&pageItem, memory + addr, 8);
		if (!(pageItem & ((uint64_t)1 << 49))) // 页表项无效
		{
			Error = true;
			sregs[Cause] = 0xf102;
			return;
		}
		addr = pageItem & 0xfffffffff; // 下级页表地址
		addr <<= 12;
	}
	tlb[vpage & 0xff] = {vpage, pageItem}; // 更新页表
}

// IOChannel相关函数
// Device
Device::Device(TYPE type, const char *name)
{
	typid = type;
	devicePtr = nullptr;
	switch (type)
	{
	case In:
		devicePtr = &std::cin;
		break;
	case Out:
		devicePtr = &std::cout;
		break;
	case RO:
		devicePtr = new std::ifstream(name);
		break;
	case WO:
		devicePtr = new std::ofstream(name);
		break;
	case RW:
		devicePtr = new std::fstream(name);
		break;
	}
}
// IOChannel
inline void IOChannel::addDevice(const char *name, uint64_t id, TYPE type) { devices[id] = new Device(type, name); }
inline void IOChannel::delDevice(uint64_t id) { delete devices[id]; }
void IOChannel::read(const size_t count)
{
	switch (devices[IMMs[MAR]]->typid)
	{
	case In: // 键盘输入
		IMMs[MDR] = reinterpret_cast<std::istream *>(devices[IMMs[MAR]]->devicePtr)->get();
		break;
	case Out: // 控制台输出
		Error = true;
		sregs[Cause] = 0xf201; // 尝试读取只写设备
		break;
	case RO: // 只读输入，只负责读入到处理器，不负责读入到内存
		(reinterpret_cast<std::ifstream *>(devices[IMMs[MAR]]->devicePtr))->read(reinterpret_cast<char *>(IMMs(MDR)), count);
		break;
	case WO: // 只写输出
		Error = true;
		sregs[Cause] = 0xf201; // 尝试读取只写设备
		break;
	case RW: // 读写端口
		(reinterpret_cast<std::fstream *>(devices[IMMs[MAR]]->devicePtr))->read(reinterpret_cast<char *>(IMMs(MDR)), count);
		break;
	}
}
void IOChannel::write(const size_t count)
{
	switch (devices[IMMs[MAR]]->typid)
	{
	case In: // 键盘输入
		Error = true;
		sregs[Cause] = 0xf202; // 尝试写入只读设备
		break;
	case Out: // 控制台输出
		(reinterpret_cast<std::ostream *>(devices[IMMs[MAR]]->devicePtr))->put(IMMs[MDR]);
		break;
	case RO: // 只读输入，只负责读入到处理器，不负责读入到内存
		Error = true;
		sregs[Cause] = 0xf202; // 尝试写入只读设备
		break;
	case WO: // 只写输出
		(reinterpret_cast<std::ofstream *>(devices[IMMs[MAR]]->devicePtr))->write(reinterpret_cast<char *>(IMMs(MDR)), count);
		break;
	case RW: // 读写端口
		(reinterpret_cast<std::fstream *>(devices[IMMs[MAR]]->devicePtr))->write(reinterpret_cast<char *>(IMMs(MDR)), count);
		break;
	}
}

// MemControl相关函数
MemControl::MemControl()
{
	cache = new Cache;
	tlb = new TLB;
	io = new IOChannel;
}
MemControl::~MemControl()
{
	delete cache;
	delete tlb;
	delete io;
}
void MemControl::read(const size_t size, bool isCommand)
{
	IMMs[MDR] = 0;
	if (!sregs[Status] & (1 << 18)) // Cache未启用
		memcpy(IMMs(MDR), memory + IMMs[MAR], size);
	else
		cache->read(isCommand);
}
void MemControl::write(const size_t size)
{
	if (!sregs[Status] & (1 << 18)) // Cache未启用
		memcpy(memory + IMMs[MAR], IMMs(MDR), size);
	else
		cache->write(size);
}
inline void MemControl::readIO()
{
	io->read(IMMs[MAR]);
}
inline void MemControl::writeIO()
{
	io->write(IMMs[MAR]);
}
inline void MemControl::addtrans(uint8_t type)
{
	if (sregs[Status] & (1 << 19)) // TLB未启用
		return;
	tlb->addtrans(type);
}